﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.Collections;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using Zbc.Utilities;
using System.Drawing.Design;

namespace Zbc.UserControl
{
    //单个菜单抽象类
    [DesignTimeVisible(false)]
    [ToolboxItem(false)]
    public abstract class MenuItemBase : Component
    {
        internal bool Selected;
        [Browsable(false)]
        [DefaultValue("")]
        [Category("常用"), Description("名称")]
        public string Name { get; set; }
        [Description("与对象关联的文本")]
        public string Text { get; set; }
        [Description("是否显示边线"), DefaultValue(true)]
        public bool ShowBorder { get; set; }
        /// 边线颜色
        /// </summary>
        [Description("边线颜色")]
        public Color BoderColor { get; set; }
        /// <summary>
        /// 选中背景色
        /// </summary>
        [Description("选中状态背景色")]
        public Color SelectBackColor { get; set; }
        /// <summary>
        /// 选中按钮字体颜色
        /// </summary>
        [Description("选中状态字体颜色")]
        public Color SelectColor { get; set; }
        [Description("是否显示阴影"), DefaultValue(true)]
        public bool ShowShadow { get; set; }
        [Description("阴影范围")]
        public int ShadowSize { get; set; }
        [Description("字体")]
        public Font Font { get; set; }
        [Description("字体颜色")]
        public Color ForeColor { get; set; }
        [Description("与对象关联的数据"), TypeConverter(typeof(StringConverter))]
        public object Tag { get; set; }
        [Description("控件可用性"), DefaultValue(true)]
        public bool Enabled { get; set; }
        [Description("控件大小范围")]
        public Rectangle Bounds { get; set; }
        /// <summary>
        /// 所属菜单集合
        /// </summary>
        internal MenuItemCollection Owner { get; set; }

        /// <summary>
        /// 所属菜单
        /// </summary> 
        [Browsable(false)]
        public CustomMenuStrip MenuStrip
        {
            get
            {
                if (Owner == null)
                    return null;
                return Owner.MenuStrip;
            }
        }

        public abstract void Draw(Graphics g, CustomMenuStrip menu, ref MenuItemCollection items);

        public abstract int GetHeight(CustomMenuStrip menu);
        public MenuItemBase Clone()
        {
            return (MenuItemBase)this.MemberwiseClone();
        }

    }

    public class TextMenuItem : MenuItemBase
    {
        [Category("常用"), Description("图标")]
        public Image Image { get; set; }
        [Category("常用"), Description("快捷键文本")]
        public string QuickKey { get; set; }
        [Category("常用"), Description("勾选状态"), DefaultValue(false)]
        public bool Checked
        {
            get { return check; }
            set
            {
                check = value;
                if (MenuStrip != null)
                    MenuStrip.OnCheckChanged(this);
            }
        }
        [Category("常用"), Description("子菜单集合"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public MenuItemCollection Items
        {
            get;
            private set;
        }

        public event Action<TextMenuItem> Click;

        private bool check;

        public TextMenuItem()
        {
            Enabled = true;
            Items = new MenuItemCollection(this);
        }
        public TextMenuItem(string txt)
        {
            Name = Text = txt;
            Enabled = true;
            Items = new MenuItemCollection(this);
        }

        public override void Draw(Graphics g, CustomMenuStrip menu, ref MenuItemCollection childItems)
        {
            //鼠标经过效果
            if (Enabled && Selected)
            {
                DrawHoverStyle(g, menu);
            }
            //勾选效果
            if (Checked)
            {
                DrawCheckMark(g, menu);
            }
            else if (Image != null)
            {
                DrawImage(g, menu);
            }
            DrawText(g, menu);
            //含子菜单的小三角以及子菜单
            if (Enabled && Items.Count > 0)
            {
                DrawTriangle(g, menu);
                if (Selected)
                    childItems = Items;
            }
        }

        private void DrawTriangle(Graphics g, CustomMenuStrip menu)
        {
            int middleY = Bounds.Y + Bounds.Height / 2;
            using (SolidBrush brush = new SolidBrush(Selected ? menu.ArrowHoverColor : menu.ArrowColor))
                g.FillPolygon(brush, new Point[] { new Point(Bounds.X + Bounds.Width - 12, middleY - 4), new Point(Bounds.X + Bounds.Width - 12, middleY + 4), new Point(Bounds.X + Bounds.Width - 8, middleY) });
        }

        private void DrawHoverStyle(Graphics g, CustomMenuStrip menu)
        {
            using (SolidBrush iBrush = new SolidBrush(menu.HoverColor))
                g.FillRectangle(iBrush, Bounds);
        }

        private void DrawCheckMark(Graphics g, CustomMenuStrip menu)
        {
            int middleY = Bounds.Y + Bounds.Height / 2;
            using (Pen iPen = new Pen(menu.MarkColor, 2))
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.DrawLines(iPen, new Point[] { new Point(Bounds.X + 9, middleY - 1), new Point(Bounds.X + 13, middleY + 3), new Point(Bounds.X + 20, middleY - 4) });
                g.SmoothingMode = SmoothingMode.Default;
            }
        }

        private void DrawImage(Graphics g, CustomMenuStrip menu)
        {
            Image image = Enabled ? Image : DrawHelper.GetGrayImage(Image);
            Rectangle imageBounds = new Rectangle(Bounds.X + (menu.ImageWidth - menu.ImageSize.Width) / 2, Bounds.Y + (Bounds.Height - menu.ImageSize.Height) / 2, menu.ImageSize.Width, menu.ImageSize.Height);
            g.DrawImage(image, imageBounds, new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
        }

        private void DrawText(Graphics g, CustomMenuStrip menu)
        {
            using (SolidBrush iBrush = new SolidBrush(Enabled ? menu.ForeColor : menu.DisableFontColor))
            {
                Rectangle rect = new Rectangle(Bounds.X + menu.ImageWidth, Bounds.Y, Owner.TextWidth, Bounds.Height);
                g.DrawString(Text, menu.Font, iBrush, rect, menu.Format);
                rect = new Rectangle(Bounds.X + menu.ImageWidth + Owner.TextWidth, Bounds.Y, Owner.QuickKeyWidth, Bounds.Height);
                g.DrawString(QuickKey, menu.Font, iBrush, rect, menu.Format);
            }
        }
        internal void OnClick()
        {
            if (Click != null)
            {
                Click(this);
            }
        }

        public override int GetHeight(CustomMenuStrip menu)
        {
            return TextRenderer.MeasureText("o", menu.Font).Height + menu.TextHeightSp;
        }
    }

    public class SplitMenuItem : MenuItemBase
    {
        const int SPLITITEM_HEIGHT = 3;
        public override void Draw(Graphics g, CustomMenuStrip menu, ref MenuItemCollection items)
        {
            //画渐变线
            DrawSplitLine(g, menu);
        }

        private void DrawSplitLine(Graphics g, CustomMenuStrip menu)
        {
            Rectangle rect = new Rectangle(Bounds.X + 2, Bounds.Y + 1, Bounds.Width - 4, 1);
            using (LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Transparent, menu.BoderColor, LinearGradientMode.Horizontal))
            {
                Color[] colors = new Color[] { Color.Transparent, menu.BoderColor, menu.BoderColor, Color.Transparent };
                ColorBlend blend = new ColorBlend();
                blend.Positions = new float[] { 0, 0.2f, 0.8f, 1 };
                blend.Colors = colors;
                brush.InterpolationColors = blend;
                g.FillRectangle(brush, rect);
            }
        }

        public override int GetHeight(CustomMenuStrip menu)
        {
            return SPLITITEM_HEIGHT;
        }
    }

    //菜单集合
    [Editor(typeof(ItemCollectionEditor), typeof(UITypeEditor))]
    public class MenuItemCollection : CollectionBase
    {
        public Rectangle Bounds { get; set; }
        internal int TextWidth { get; set; }
        internal int QuickKeyWidth { get; set; } 
        /// <summary>
        /// 多级菜单箭头宽度
        /// </summary>
        internal readonly int ArrowWidth = 10;
        internal MenuItemBase Owner
        {
            get;
            private set;
        }

        public MenuItemCollection(MenuItemBase owner = null)
        {
            Owner = owner;
        }

        CustomMenuStrip menuStrip;
        internal CustomMenuStrip MenuStrip
        {
            get
            {
                if (menuStrip == null)
                {
                    return Owner.MenuStrip;
                }
                return menuStrip;
            }
            set
            {
                menuStrip = value;
            }
        }

        public MenuItemBase this[int index]
        {
            get
            {
                if (index >= 0 && index < List.Count)
                    return ((MenuItemBase)List[index]);
                return null;
            }
            set { List[index] = value; }
        }

        public TextMenuItem this[string name]
        {
            get
            {
                foreach (MenuItemBase item in this.List)
                {
                    if (item.Name == name) return (TextMenuItem)item;
                }
                return null;
            }
        }

        public void Draw(Graphics g, CustomMenuStrip menu)
        {
            //画背景
            DrawBackGround(g);
            //画阴影
            DrawShadow(g, menu, 7);
            //画菜单
            MenuItemCollection items = null;
            foreach (MenuItemBase item in List)
            {
                item.Draw(g, menu, ref items);
            }
            if (items != null)
            {
                items.Draw(g, menu);
            }
        }

        private void DrawBackGround(Graphics g)
        {
            //背景色
            g.FillRectangle(new SolidBrush(MenuStrip.BackColor), Bounds);
        }

        public void CalcBounds(Point location, CustomMenuStrip menu)
        {
            int width = 0;
            int height = 0;
            CalcMenuSize(menu, ref width, ref height);
            AdjustLocation(ref location, height, width);
            this.Bounds = PathHelper.ExpandRect(new Rectangle(location, new Size(width, height)), CustomMenuStrip.EXPAND_LENGTH);
            height = 0;
            foreach (MenuItemBase item in List)
            {
                item.Bounds = new Rectangle(location.X, location.Y + height, width, item.GetHeight(menu));
                if (item is TextMenuItem && ((TextMenuItem)item).Items.Count > 0)
                {
                    ((TextMenuItem)item).Items.CalcBounds(new Point(item.Bounds.Right, item.Bounds.Top), menu);
                }
                height += item.Bounds.Height;
            }
        }

        private void AdjustLocation(ref Point location, int height, int width)
        {
            Rectangle workArea = Screen.AllScreens[0].WorkingArea;
            if (location.X + width >= workArea.Right)
                location.X -= width + (Owner == null ? 0 : Owner.Bounds.Width);
            if (location.Y + height >= workArea.Bottom)
                location.Y = workArea.Bottom - height;
        }

        private void CalcMenuSize(CustomMenuStrip menu, ref int maxWidth, ref int maxHeight)
        {
            foreach (MenuItemBase item in List)
            {
                if (item is TextMenuItem)
                {
                    var txtItem = item as TextMenuItem;
                    int textWidth = TextRenderer.MeasureText(txtItem.Text, menu.Font).Width + menu.TextWidthSp;
                    int quickKeyWidth = TextRenderer.MeasureText(txtItem.QuickKey, menu.Font).Width + menu.QuickKeyWidthSp;
                    if (textWidth > TextWidth)
                        TextWidth = textWidth;
                    if (quickKeyWidth > QuickKeyWidth)
                        QuickKeyWidth = quickKeyWidth;
                }
                maxWidth = menu.ImageWidth + TextWidth + QuickKeyWidth;
                maxHeight += item.GetHeight(menu);
            }
        }

        protected override void OnInsertComplete(int index, object value)
        {
            base.OnInsertComplete(index, value);
            ((MenuItemBase)value).Owner = this;
        }

        private void DrawShadow(Graphics g, CustomMenuStrip menu, int radius)
        {
            if (!menu.ShowShadow) return;
            if (Bounds.Height == 0) return;
            Color[] colors1 = new Color[] { Color.Transparent, Color.FromArgb(21, menu.ShadeColor), menu.ShadeColor };
            ColorBlend blend1 = new ColorBlend();
            blend1.Positions = new float[] { 0, 0.61f, 1 };
            blend1.Colors = colors1;
            Color[] colors2 = new Color[] { menu.ShadeColor, Color.FromArgb(21, menu.ShadeColor), Color.Transparent };
            ColorBlend blend2 = new ColorBlend();
            blend2.Positions = new float[] { 0, 0.39f, 1 };
            blend2.Colors = colors2;

            var px1 = new int[] { Bounds.Left - radius, Bounds.Right, Bounds.Left, Bounds.Left };
            var py1 = new int[] { Bounds.Top, Bounds.Top, Bounds.Bottom, Bounds.Top - radius };
            var px2 = new int[] { radius, Bounds.Width };
            var py2 = new int[] { Bounds.Height, radius };
            var modeList = new LinearGradientMode[] { LinearGradientMode.Horizontal, LinearGradientMode.Vertical };
            var blendList = new ColorBlend[] { blend1, blend2, blend2, blend1 };
            var list1 = new int[] { Bounds.Right, Bounds.Left, Bounds.Left, Bounds.Right };
            var list2 = new int[] { Bounds.Bottom, Bounds.Top };
            for (int i = 0; i < 4; i++)
            {
                Rectangle shadowRect = new Rectangle(px1[i], py1[i], px2[i / 2], py2[i / 2]);
                using (LinearGradientBrush brush = new LinearGradientBrush(shadowRect, Color.Transparent, Color.Transparent, modeList[i / 2]))
                {
                    brush.InterpolationColors = blendList[i];
                    g.FillRectangle(brush, shadowRect);
                }
                using (GraphicsPath path = new GraphicsPath())
                {
                    Rectangle selRect = new Rectangle(list1[i] - radius, list2[i / 2] - radius, radius * 2, radius * 2);
                    path.AddEllipse(selRect);
                    using (PathGradientBrush pathBrush = new PathGradientBrush(path))
                    {
                        pathBrush.InterpolationColors = blend1;
                        g.FillPie(pathBrush, selRect, i * 90, 90);
                        pathBrush.Dispose();
                    }
                    path.Dispose();
                }
            }
        }

        public MenuItemBase Add(MenuItemBase item)
        {
            List.Add(item);
            return item;
        }

        public void Remove(MenuItemBase item)
        {
            List.Remove(item);
        }
    }
}
